---
sidebar_position: 6
---

# Пример 5. Декомпозиция проекта при работе с множествами объектов

## Исходная задача

Предположим, что у нас есть класс `Rectangle`, в котором описаны некоторые его свойства и методы взаимодействия с ними:

```cpp
#include <iostream>

class Rectangle {
private:
    int a_, b_;
public:
    Rectangle() {}

    Rectangle(int a, int b) {
        this->a_ = a;
        this->b_ = b;
    }

    int getA() {
        return a_;
    }

    int getB() {
        return b_;
    }

    void setA(int a) {
        this->a_ = a;
    }

    void setB(int b) {
        this->b_ = b;
    }

    void set(int a, int b) {
        setA(a);
        setB(b);
    }
};

int main() {
    Rectangle *rec = new Rectangle(1, 3);
    std::cout << "I know about the rectangle. It has: " 
              << std::endl;
    std::cout << " a=" << rec->getA() << std::endl;
    std::cout << " b=" << rec->getB() << std::endl;
    delete rec;
}
```

Обратите внимание, что названия `private` переменных имеют постфикс `_`, чтобы отличать их от общедоступных. Также названий переменных и методов используется верблюжий (`CamelCase`) стиль.

:::info
О лучших стилистических практиках рассказано [здесь](https://github.com/cpp-best-practices/cppbestpractices/blob/master/03-Style.md).
:::

Предположим, что нам поставили следующую **задачу**: обрабатывать в программе множество объектов класса `Rectangle`.

## Подход 1: хранение множества объектов в месте его использования

### Объявление множества в функции `main`, а его обработчиков - в `main.cpp`

Первое предположение о решении задачи, которое может прийти на ум, может быть желанием хранить нужное нам множество там, где мы с ним будем работать. В нашем случае делать это в функции `main`. В том же файле, где содержится функция `main` (`main.cpp`), расположить функции работы с нашим множеством. Итоговый результат будет таким:

```cpp
#include <iostream>

class Rectangle {
private:
    int a_, b_;
public:
    Rectangle() {}

    Rectangle(int a, int b) {
        this->a_ = a;
        this->b_ = b;
    }

    int getA() {
        return a_;
    }

    int getB() {
        return b_;
    }

    void setA(int a) {
        this->a_ = a;
    }

    void setB(int b) {
        this->b_ = b;
    }

    void set(int a, int b) {
        setA(a);
        setB(b);
    }

};

// highlight-start
void showRectangles(Rectangle *arr, int n) {
    for (int i = 0; i < n; i++) {
        std::cout << "I know about the rectangle # " << i
                  << ". It has: " << std::endl;
        std::cout << " a=" << arr[i].getA() << std::endl;
        std::cout << " b=" << arr[i].getB() << std::endl;
    }
}

int calculateAreas(Rectangle *arr, int n) {
    int sum = 0;
    for (int i = 0; i < n; i++) {
        sum += arr[i].getA() * arr[i].getB();
    }
    return sum;
}
// highlight-end

int main() {
    // highlight-start
    int n = 2;
    Rectangle *arr = new Rectangle[n];
    arr[0].set(1, 3);
    arr[1].set(4, 6);

    showRectangles(arr, n);
    std::cout << std::endl;
    std::cout << "Sum of the areas of all rectangles: "
              << calculateAreas(arr, n) << std::endl;
    delete[] arr;
    // highlight-end
}
```

Функция `showRectangles` у нас показывает информацию о всех прямоугольниках в множества, `calculateAreas` - суммирует площади прямоугольников из множества.

А что делать, если в нашей программе мы будем работать не только с объектами класса `Rectangle`, но и с `Circle`, `Triangle`, `Square`? Тогда в обработчики множеств объектов соответствующих классов нам также придется размещать в `main.cpp`. Чем это грозит?

1. Нам придется **придумывать длинные названия функций**, которые выполняют одни и те же действия, но для разных объектов. Например, для расчеты суммы площадей множеств нам придется использовать названия `calculateAreasRectangle`, `calculateAreasTriangle` и т.д. Но в C++ имеется **механизм перегрузки функций**, который позволит нам использовать имя функции `calculateAreas` для разных множеств.

   [Перегрузка функций](https://foxford.ru/wiki/informatika/peregruzka-funktsiy-v-s) — это механизм С++, благодаря которому функции с разным количеством или типами параметров могут иметь одинаковое имя (идентификатор).

2. Даже если мы применили механизм перегрузки функций, мы сталкиваемся с **ростом объема** `main.cpp`. В [лабораторной работе №1](/docs/labs/lab1) мы познакомились о том, что для декомпозиции проекта можно использовать механизм статических библиотек классов. Значит мы можем унести наши обработчики множеств на уровень статических библиотек и разместить их по соседству с объявлением соответствующих классов или в самих классах.

### Объявление множества в функции `main`, а его обработчики сделать методами объектов класса `Rectangle`

Остановимся на том, что функции работы с множествами мы будем размещать в самих классах. Для примера мы продолжим использовать файл `main.cpp`. Для класса `Rectangle` результат наших преобразований будет выглядеть следующим образом:

```cpp
#include <iostream>

class Rectangle {
private:
    int a_, b_;
public:
    Rectangle() {}

    Rectangle(int a, int b) {
        this->a_ = a;
        this->b_ = b;
    }

    int getA() {
        return a_;
    }

    int getB() {
        return b_;
    }

    void setA(int a) {
        this->a_ = a;
    }

    void setB(int b) {
        this->b_ = b;
    }

    void set(int a, int b) {
        setA(a);
        setB(b);
    }

    // highlight-start
    void showRectangles(Rectangle *arr, int n) {
        for (int i = 0; i < n; i++) {
            std::cout << "I know about the rectangle # " << i
                      << ". It has: " << std::endl;
            std::cout << " a=" << arr[i].getA() << std::endl;
            std::cout << " b=" << arr[i].getB() << std::endl;
        }
    }

    int calculateAreas(Rectangle *arr, int n) {
        int sum = 0;
        for (int i = 0; i < n; i++) {
            sum += arr[i].getA() * arr[i].getB();
        }
        return sum;
    }
    // highlight-end
};

int main() {
    int n = 2;
    Rectangle *arr = new Rectangle[n];
    arr[0].set(1, 3);
    arr[1].set(4, 6);

    // highlight-start
    arr[0].showRectangles(arr, n);
    // highlight-end
    std::cout << std::endl;
    std::cout << "Sum of the areas of all rectangles: "
              // highlight-start
              << arr[0].calculateAreas(arr, n) << std::endl;
    // highlight-end
    delete[] arr;
}
```

В этой реализации мы сталкиваемся с несколькими проблемами:

- **Первая проблема** данных методов, о которой мы можем увидеть, это то, что **вызов методов работы с массивами происходит через конкретный объект**. Иными словами, нам для вызова метода работы с массивом нужно обратиться к какому-то объекту из этого массива и вызвать у него требуемый метод.
- **Вторая вытекающая проблема** заключается в том, что методы работы с массивами зачем-то имеют доступу к конкретному экземпляра класса, через который вызываются данные методы. А это может повлечь **проблему безопасной работы с конкретным объектом** в случае внесения каких-то изменений по неосторожности. Пример, где вы можете столкнуться с проблемами безопасного управления данными: сортировка множества.

Решить эти проблемы можно достаточно просто: сделать эти методы [статическими методами класса](https://habr.com/ru/post/527044/)!

Демонстрация использования статических методов представлена в [примере 3](Example3.md).

### Объявление множества в функции `main`, а его обработчики сделать статическими методами класса `Rectangle`

Результат применения статических методов выглядит следующим образом:

```cpp
#include <iostream>

class Rectangle {
private:
    int a_, b_;
public:
    Rectangle() {}

    Rectangle(int a, int b) {
        this->a_ = a;
        this->b_ = b;
    }

    int getA() {
        return a_;
    }

    int getB() {
        return b_;
    }

    void setA(int a) {
        this->a_ = a;
    }

    void setB(int b) {
        this->b_ = b;
    }

    void set(int a, int b) {
        setA(a);
        setB(b);
    }

    // highlight-start
    static void showRectangles(Rectangle *arr, int n) {
    // highlight-end
        for (int i = 0; i < n; i++) {
            std::cout << "I know about the rectangle # " << i 
                      << ". It has: " << std::endl;
            std::cout << " a=" << arr[i].getA() << std::endl;
            std::cout << " b=" << arr[i].getB() << std::endl;
        }
    }

    // highlight-start
    static int calculateAreas(Rectangle *arr, int n) {
    // highlight-end
        int sum = 0;
        for (int i = 0; i < n; i++) {
            sum += arr[i].getA() * arr[i].getB();
        }
        return sum;
    }
};

int main() {
    int n = 2;
    Rectangle *arr = new Rectangle[n];
    arr[0].set(1, 3);
    arr[1].set(4, 6);

    // highlight-start
    Rectangle::showRectangles(arr, n);
    // highlight-end
    std::cout << std::endl;
    std::cout << "Sum of the areas of all rectangles: " 
    // highlight-start
              << Rectangle::calculateAreas(arr, n) << std::endl;
    // highlight-end
    delete[] arr;
}
```

Теперь вызов любого метода работы с множеством объектов никак не нарушит данные объектов массива.

Мы рассмотрели один из возможных подходов для декомпозиции проекта, в котором происходит обработка множества объектов класса. Этот подход можно применить для выполнения данной лабораторной работы.

В [статье](/blog/2023-03-02-workshop-decomposition) предлагается еще один подход для декомпозиции через класс-агрегат. В этой же статье демонстрируется способ работы с динамическим массивом и изменение его размера.

### Декомпозиция файла `main.cpp`

Как ранее упоминалось, для демонстрации не использовалось выделение класса `Rectangle` в отдельные `rectangle.cpp` и `rectangle.h`. Если это сделать, то результат получится такой, как показан в репозитории по [ссылке](https://hub.mos.ru/iu5bmstu/lections-and-labs/cpp2/textbook/-/tree/main/src/labs/lab2/decomposition-part1).

### Создание статической библиотеки `rectangle`

После того, как мы `Rectangle` выделили в `rectangle.cpp` и `rectangle.h`, мы можем их упаковать в статическую библиотеку. Если это сделать, то результат получится такой, как показан в репозитории по [ссылке](https://hub.mos.ru/iu5bmstu/lections-and-labs/cpp2/textbook/-/tree/main/src/labs/lab2/decomposition-part2).
